/*
 * imageio.c
 *
 *  Created on: Mar 28, 2009
 *      Author: gustavo
 */
#include <stdio.h>

#include "../../matrix/matrix.h"
#include "../image.h"
#include "imageio.h"
#include "../pointop/threshold.h"

void imp_image_save_matrix_as_image(ImpMatrix * M, boolean normalize, char * fname) {
	ImpMatrix * Tmp = NULL;
	ImpImage * Img = NULL;

	if (normalize) {
		imp_matrix_get_normailzed(M, &Tmp, 0, 255);
		imp_image_mat2gray(Tmp, &Img);
		imp_image_save_pgm(Img, fname);
		
		imp_object_delete(Tmp);
		imp_image_delete(Img);
		
	} else {
		imp_image_threshold(M, 255.0, 0.0, 255.0, IMP_THRESHOLD_TRUNCATE_UP, &Tmp);
		imp_image_threshold(Tmp, 0.0, 0.0, 255.0, IMP_THRESHOLD_TRUNCATE_DOWN, NULL);
		imp_image_mat2gray(Tmp, &Img);
		imp_image_save_pgm(Img, fname);
		
		imp_object_delete(Tmp);
		imp_image_delete(Img);
	}	
}

int imp_image_save_pgm(ImpImage * im, char * fname) {
	FILE* fp;

	int width = im->width;
	int height = im->height;
	unsigned char * img = im->data;


	if ((fp = fopen(fname, "w")) == NULL) {
		fprintf(stderr, "Could not open file %s for writing.\n", fname);
		return 0;
	}

	fprintf(fp, "P5\n# CREATOR: ImpPack.\n%d %d\n255\n", width, height);

	int index;
	int size = width * height;
	for (index = 0; index < size; ++index) {
		putc(img[index], fp);
	}

	fflush(fp);
	fclose(fp);
	return 1;
}

int imp_image_save_ppm(ImpImage * im, char * fname) {
	FILE* fp;

	int width = im->width;
	int height = im->height;
	unsigned char * img = im->data;

	if ((fp = fopen(fname, "w")) == NULL) {
		fprintf(stderr, "Could not open file %s for writing.\n", fname);
		return 0;
	}

	fprintf(fp, "P6\n# CREATOR: ImpPack.\n%d %d\n255\n", width, height);

	int index;
	int size = width * height;
	for (index = 0; index < size; ++index) {
		putc(img[index * 3 + 0], fp);
		putc(img[index * 3 + 1], fp);
		putc(img[index * 3 + 2], fp);
	}

	fflush(fp);
	fclose(fp);
	return 1;
}

ImpImage * imp_image_read_ppm(char * filename) {
	int width;
	int height;
	int idx;
	int size;
	int max_value;
	char ch;
	int ret;

	FILE * file;
	ImpImage * img;

	file = fopen(filename, "rb");
	if (file == 0) {
		//print_error(stderr, "File binary open error.");
		//fclose ( f );
		return NULL;
	}

	/* Ler cabecalho */
	if (fscanf(file, "P%c\n", &ch) != 1 || ch != '6') {
		//print_error(stderr, "Invalid file.");
		fclose(file);
		return NULL;
	}

	/* Desconsidera comentarios */
	ch = getc(file);
	while (ch == '#') {
		do {
			ch = getc(file);
		} while (ch != '\n'); /* Le ate o final da linha */
		ch = getc(file);
	}
	ungetc(ch, file);
	ret = fscanf(file, "%d %d\n", &width, &height);
	/* idx esta funcionando como uma variavel inteira temportaria */
	ret = fscanf(file, "%d\n", &idx);
	max_value = idx;

	/* Salva os parametros da imagem */
	img = imp_image_new(width, height, IMP_IMAGE_TYPE_RGB);

	if (img == NULL) {
		//print_error(stderr, "Unable to alloc memory to image.");
		fclose(file);
		return NULL;
	}

	/* Leitura dos pixels */
	size = img->width * img->height * img->nchannels;
	for (idx = 0; idx < size; idx++) {
		ret = fread(&ch, sizeof (char), 1, file);
		//img->data[idx] = (float) ch;
		img->data[idx] = (unsigned char) ch;
	}
	/* fread ( img->data, sizeof (char), img->width * img->height * img->n_channels, f ); */

	fclose(file);
	return img;
}

ImpImage * imp_image_read_pgm(char * filename) {
	int width;
	int height;
	int idx;
	int size;
	real max_value;
	char ch;
	int ret;

	FILE * f;
	ImpImage * img;

	f = fopen(filename, "rb");
	if (f == 0) {
		//print_error(stderr, "File binary open error.");
		//fclose ( f );
		return NULL;
	}

	/* Ler cabecalho */
	if (fscanf(f, "P%c\n", &ch) != 1 || ch != '5') {
		//print_error(stderr, "Invalid file.");
		fclose(f);
		return NULL;
	}

	/* Desconsidera comentarios */
	ch = getc(f);
	while (ch == '#') {
		do {
			ch = getc(f);
		} while (ch != '\n'); /* Le ate o final da linha */
		ch = getc(f);
	}
	ungetc(ch, f);
	ret = fscanf(f, "%d %d\n", &width, &height);
	/* idx esta funcionando como uma variavel inteira temportaria */
	ret = fscanf(f, "%d\n", &idx);
	max_value = (real) idx;

	/* Salva os parametros da imagem */
	img = imp_image_new(width, height, IMP_IMAGE_TYPE_GRAY);

	if (img == NULL) {
		//print_error(stderr, "Unable to alloc memory to image.");
		fclose(f);
		return NULL;
	}

	/* Leitura dos pixels */
	size = img->width * img->height * img->nchannels;
	for (idx = 0; idx < size; idx++) {
		ret = fread(&ch, sizeof (char), 1, f);
		img->data[idx] = (float) ch;
	}
	/* fread ( img->data, sizeof (char), img->width * img->height * img->n_channels, f ); */

	fclose(f);
	return img;
}

/*= Estruturas do formato BMP ================================================*/
typedef struct _BMP_header {
	char bfType[2]; // "BMv?"
	int bfSize; // Tamanho do arquivo
	short int bfReserved1;
	short int bfReserved2;
	int bfOffsetBits; // Deslocamento (em bytes) para o inicio da area de dados
} BMP_header;

typedef struct _BMP_info_header {
	int biSize; // Tamanho deste cabecalho. Sempre 40 bytes ou 28h
	int biWidth; // Largura da imagem
	int biHeight; // Altura da imagem
	short int biPlanes; // Numero de planos da imagem, sempre = 1
	short int biBitCount; // Numero de bits por pixel (1,4,8,24,32)
	int biCompression; // Tipo de compressao usada.
	//	- 0: BI_RGB; Sem compressao
	//	- 1: BI_RLE8; compressao RLE 8 bits
	//	- 2: BI_RLE4; compressao RLE 4 bits
	int biSizeImage; // Tamanho da imagem em bytes
	int biXPixelsPerMeter; // Resolucao horizontal. Pixels/Metro
	int biYPixelsPerMeter; // Resolucao vertical. Pixels/Metro
	int biClrUsed; // Numero de cores usada na imagem. Se for 0, indica o uso
	// do miximo de cores possivel: 2^BiBitCount
	int biClrImportant; // Numero de cores realmente usada na imagem. Se 0, indica
	// que todas sao importantes
	char * Paleta; // Paleta de cores: 4bytes x nimero de cores.
	// Na ordem: Blue, Green, Red, ZERO
} BMP_info_header;

/*===========================================================================================*/

ImpImage * imp_image_read_bmp(char * filename) {
	FILE * f;
	BMP_header bmp_header;
	BMP_info_header infoHeader;

	ImpImage * img;

	int idx;
	char px;
	int row, col, alignBytes;
	int scanLine, offLine;
	int t;

	f = fopen(filename, "rb");
	if (f == 0) {
		fprintf(stderr, "File binary open error.");
		return NULL;
	}

	t = fread(&bmp_header.bfType, sizeof (char), 2, f);
	t = fread(&bmp_header.bfSize, sizeof (int), 1, f);
	t = fread(&bmp_header.bfReserved1, sizeof (short int), 1, f);
	t = fread(&bmp_header.bfReserved2, sizeof (short int), 1, f);
	t = fread(&bmp_header.bfOffsetBits, sizeof (int), 1, f);

	t = fread(&infoHeader.biSize, sizeof (int), 1, f);
	t = fread(&infoHeader.biWidth, sizeof (int), 1, f);
	t = fread(&infoHeader.biHeight, sizeof (int), 1, f);
	t = fread(&infoHeader.biPlanes, sizeof (short int), 1, f);
	t = fread(&infoHeader.biBitCount, sizeof (short int), 1, f);
	t = fread(&infoHeader.biCompression, sizeof (int), 1, f);
	t = fread(&infoHeader.biSizeImage, sizeof (int), 1, f);
	t = fread(&infoHeader.biXPixelsPerMeter, sizeof (int), 1, f);
	t = fread(&infoHeader.biYPixelsPerMeter, sizeof (int), 1, f);
	t = fread(&infoHeader.biClrUsed, sizeof (int), 1, f);
	t = fread(&infoHeader.biClrImportant, sizeof (int), 1, f);

	if (infoHeader.biBitCount != 24) {
		fprintf(stderr, "BMP file is not 24 bits. This version supports only 24 bits.");
		fclose(f);
		return NULL;
	}

	if (infoHeader.biCompression != 0) {
		fprintf(stderr, "BMP file is compressed.");
		fclose(f);
		return NULL;
	}

	/* To BMP 24 bits, the color paleta is null */
	img = imp_image_new(infoHeader.biWidth, infoHeader.biHeight, IMP_IMAGE_TYPE_RGB);
	/*
			img->width = infoHeader.biWidth;
			img->height = infoHeader.biHeight;
			img->channels = 3;
			img->palette = IMP_IMAGE_TYPE_RGB;
			img->size = img->width * img->height * (img->channels);
			img->data = (char *) calloc(1, sizeof (real) * img->size);
	 */


	scanLine = infoHeader.biWidth * 3;
	offLine = 0;
	while ((scanLine % 4) != 0) {
		scanLine++;
		offLine++;
	}

	for (row = img->height - 1; row >= 0; row--) {
		for (col = 0; col < img->width; col++) {

			idx = (row * img->width + col) * 3;

			t = fread(&px, sizeof (char), 1, f);
			img->data[ idx + 2 ] = px;

			t = fread(&px, sizeof (char), 1, f);
			img->data[ idx + 1 ] = px;

			t = fread(&px, sizeof (char), 1, f);
			img->data[ idx ] = px;

			/*
									t = fread(&px, sizeof (char), 1, f);
									pPX(img, row, col, 2) = (real) px;
									t = fread(&px, sizeof (char), 1, f);
									pPX(img, row, col, 1) = (real) px;
									t = fread(&px, sizeof (char), 1, f);
									pPX(img, row, col, 0) = (real) px;
			 */
		}
		for (col = 0; col < offLine; col++) {
			t = fread(& (alignBytes), sizeof (char), 1, f);
		}
	}

	fclose(f);
	return img;
}

int imp_image_save_bmp(ImpImage * img, char *filename) {
	BMP_header fileHeader;
	BMP_info_header infoHeader;
	FILE *file;
	int scanLine, offLine;
	long int col, row;
	char blackColor = 0;
	char px;
	int idx;

	fileHeader.bfType[0] = 'B';
	fileHeader.bfType[1] = 'M';
	fileHeader.bfReserved1 = 0;
	fileHeader.bfReserved2 = 0;
	fileHeader.bfOffsetBits = 14 + 40;

	infoHeader.biSize = 40;
	infoHeader.biWidth = img->width;
	infoHeader.biHeight = img->height;
	infoHeader.biPlanes = 1;
	infoHeader.biBitCount = 24;
	infoHeader.biCompression = 0;
	infoHeader.biSizeImage = 0;
	infoHeader.biXPixelsPerMeter = 0;
	infoHeader.biYPixelsPerMeter = 0;
	infoHeader.biClrUsed = 0;
	infoHeader.biClrImportant = 0;

	scanLine = img->width * 3;
	offLine = 0;
	while ((scanLine % 4) != 0) {
		scanLine++;
		offLine++;
	}

	fileHeader.bfSize = fileHeader.bfOffsetBits + (scanLine * img->height);

	file = fopen(filename, "wb");
	if (file == NULL) return -1;

	fwrite(& (fileHeader.bfType), sizeof (char), 2, file);
	fwrite(& (fileHeader.bfSize), sizeof (int), 1, file);
	fwrite(& (fileHeader.bfReserved1), sizeof (short int), 1, file);
	fwrite(& (fileHeader.bfReserved2), sizeof (short int), 1, file);
	fwrite(& (fileHeader.bfOffsetBits), sizeof (int), 1, file);

	fwrite(& (infoHeader.biSize), sizeof (int), 1, file);
	fwrite(& (infoHeader.biWidth), sizeof (int), 1, file);
	fwrite(& (infoHeader.biHeight), sizeof (int), 1, file);
	fwrite(& (infoHeader.biPlanes), sizeof (short int), 1, file);
	fwrite(& (infoHeader.biBitCount), sizeof (short int), 1, file);
	fwrite(& (infoHeader.biCompression), sizeof (int), 1, file);
	fwrite(& (infoHeader.biSizeImage), sizeof (int), 1, file);
	fwrite(& (infoHeader.biXPixelsPerMeter), sizeof (int), 1, file);
	fwrite(& (infoHeader.biYPixelsPerMeter), sizeof (int), 1, file);
	fwrite(& (infoHeader.biClrUsed), sizeof (int), 1, file);
	fwrite(& (infoHeader.biClrImportant), sizeof (int), 1, file);


	for (row = img->height - 1; row >= 0; row--) {
		for (col = 0; col < img->width; col++) {

			idx = (row * img->width + col) * 3;

			px = img->data[idx + 2];
			fwrite(&px, sizeof (char), 1, file);
			px = img->data[idx + 1];
			fwrite(&px, sizeof (char), 1, file);
			px = img->data[idx];
			fwrite(&px, sizeof (char), 1, file);
			/*
									px = (char) pPX(img, row, col, 2);
									fwrite(&px, sizeof (char), 1, file);
									px = (char) pPX(img, row, col, 1);
									fwrite(&px, sizeof (char), 1, file);
									px = (char) pPX(img, row, col, 0);
									fwrite(&px, sizeof (char), 1, file);
			 */
		}
		for (col = 0; col < offLine; col++) {
			fwrite(& (blackColor), sizeof (char), 1, file);
		}
	}
	fclose(file);
	return 1;
}
